home *** CD-ROM | disk | FTP | other *** search
/ Mac Mania 2 / MacMania 2.toast / Demo's / Tools&Utilities / Programming / MPS disk 1.0.1 / Chapter 04 / Neat Stuff.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-05-19  |  23.7 KB  |  671 lines  |  [TEXT/KAHL]

  1. #include "Neat Stuff.h"
  2. #include "Progress Indicator.h"
  3. #include "Standard Stuff.h"
  4.  
  5. /*******************************************************************************
  6.  
  7.     On dialog memory management:
  8.  
  9.     Normally, there is one and only one copy of each kind of dialog in your
  10.     application. When creating these dialogs, there are three approaches we
  11.     can take. The first is to create these dialogs as needed and to dispose of
  12.     them when we are done. The second is to create all of these dialogs at
  13.     application startup time and keep them around for the life of the program.
  14.     When they aren’t needed, they are simply hidden. One advantage to this is
  15.     that dialogs reappear where the user left them. A third approach lies
  16.     somewhere between the first two approaches, where the dialogs are created
  17.     as needed, but are hidden when no longer needed. We take this third
  18.     approach here.
  19.  
  20.  
  21.     On handling movable-modal dialogs:
  22.  
  23.     Movable-modal dialogs are hybrid windows. They mostly look like modeless
  24.     dialogs in that they have a drag region and title bar, but they also have
  25.     the bold rectangle framing them reminiscent of modal dialogs.
  26.  
  27.     The purpose of movable-modal dialogs is to allow most of the flexibility
  28.     of modeless dialogs in cases where you would normally display a modal
  29.     dialog. The benefits the user gains are that they can move the dialog
  30.     around so that they can see parts of their other windows, and that they
  31.     can switch to other running applications. At the same time, they are in a
  32.     semi-modal state in that they can’t select any other windows in the
  33.     application, and most menu items are disabled.
  34.  
  35.     Handling movable-modal dialogs is kind of tricky.  Because the user is
  36.     allowed to switch to other applications, and because the application must
  37.     update background windows it owns as the dialog is moved around, the
  38.     application _cannot_ call ModalDialog on movable-modal windows. This means
  39.     that another approach needs to be taken.
  40.  
  41.     Fortunately, an alternate solution is not very difficult to implement. The
  42.     first thing to do is define a global variable that gets set to TRUE when a
  43.     movable-modal dialog is shown. In our case, this variable is called
  44.     “gInModalState.” When that variable is set to TRUE, two things happen.
  45.     First, our event handling routine makes a special examination of
  46.     mouse-down events. Only mouse-downs in the movable-modal window and in the
  47.     menubar are allowed. Second, our menu setup routine disables most of the
  48.     menu items.
  49.  
  50. *******************************************************************************/
  51.  
  52. /*******************************************************************************
  53.  
  54.     Global Variables.
  55.  
  56. *******************************************************************************/
  57.  
  58. short    gCurrentRadio;                /*    Item number of the currently selected
  59.                                         radio button. */
  60.  
  61. Boolean    gCurrentFlippedState;        /*    The current state of our command-key
  62.                                         hints. In other words, are they
  63.                                         currently displayed, or hidden. */
  64.  
  65. Boolean    gPeriodicTask = FALSE;        /*    Set to TRUE when we are performing
  66.                                         some periodic task every time we go
  67.                                         through the event loop. */
  68.  
  69. DialogPtr    gModelessDialog = NIL;
  70. DialogPtr    gMovableModalDialog = NIL;
  71.  
  72.  
  73. /*******************************************************************************
  74.  
  75.     DoDialogHit
  76.  
  77.     Handles any events on modeless dialogs.
  78.  
  79. *******************************************************************************/
  80. void    DoDialogHit(DialogPtr dlg, short item)
  81. {
  82.     if (item == ok) {
  83.         HideWindow(dlg);
  84.         if (dlg == gMovableModalDialog)
  85.             gInModalState = FALSE;
  86.         AdjustMenus();
  87.     }
  88. }
  89.  
  90.  
  91. /*******************************************************************************
  92.  
  93.     DoMainLoopTasks
  94.  
  95.     Do any tasks that need to be performed regularly every time we go through
  96.     the main event loop.
  97.  
  98. *******************************************************************************/
  99. void    DoMainLoopTasks(void)
  100. {
  101.     if (gPeriodicTask) {
  102.         gPeriodicTask = !SetProgressDelta(1);
  103.         if (!gPeriodicTask) {
  104.             HideWindow(gMovableModalDialog);
  105.             gInModalState = FALSE;
  106.             AdjustMenus();
  107.         }
  108.     }
  109. }
  110.  
  111.  
  112. /*******************************************************************************
  113.  
  114.     DoMusicMaker
  115.  
  116.     Show the Music Maker modal dialog box. We do a few things here. First, we
  117.     load in the dialog. We have it marked as invisible in the resource so we
  118.     can futz with it a little bit before showing it on the screen. Next, we
  119.     set one of the radio buttons (you always need at least _one_ radio button
  120.     marked). We also fiddle with the items that get flipped when we hold down
  121.     the command key. Since we are just bringing up the dialog, we set those
  122.     items to their “command-key-is-not-down” state.
  123.  
  124.     Once that is done, we can show the dialog. To do this, we simply use
  125.     ShowWindow. After all, a DialogPtr is the same as a WindowPtr. When the
  126.     window is shown, we call ModalDialog and let the System handle mouse downs
  127.     and keypresses. When something interesting happens, ModalDialog  returns,
  128.     telling us the number of the item that was selected. In other words, if
  129.     the user clicked on the OK button, we receive the ID number of the OK
  130.     button.
  131.  
  132.     We look at the item number and decide what to do next. If a radio button
  133.     is clicked on, we select it and deselect the previous button. If a
  134.     checkbox is selected, we toggle its setting. If the OK or Cancel buttons
  135.     are hit, we close the dialog box and exit this function.
  136.  
  137. *******************************************************************************/
  138. void    DoMusicMaker(void)
  139. {
  140.     short        itemHit;
  141.     DialogPtr    ourDialog;
  142.  
  143.     ourDialog = GetNewDialog(kMusicMakerDialog, NIL, (WindowPtr) -1);
  144.  
  145.     gCurrentRadio = 0;
  146.     SetRadioButton(ourDialog, kFirstRadio);
  147.  
  148.     gCurrentFlippedState = TRUE;        /* To force the update in ... */
  149.     FlipItems(ourDialog, FALSE);        /* ... this call to FlipItems. */
  150.  
  151.     ShowWindow(ourDialog);
  152.     do {
  153.         ModalDialog(OurFilter, &itemHit);
  154.         if ((itemHit >= kFirstRadio) && (itemHit <= kLastRadio))
  155.             SetRadioButton(ourDialog, itemHit);
  156.         else if ((itemHit >= kFirstCheckBox) && (itemHit <= kLastCheckBox))
  157.             ToggleCheckBox(ourDialog, itemHit);
  158.     } while ((itemHit != ok) && (itemHit != cancel));
  159.  
  160.     DisposeDialog(ourDialog);
  161. }
  162.  
  163.  
  164. /*******************************************************************************
  165.  
  166.     OurFilter
  167.  
  168.     This routine is passed to ModalDialog as a “filter procedure”. This means
  169.     that it is called in the core loop of ModalDialog to handle any custom
  170.     actions. For instance, in this routine, we handle certain command-key
  171.     combinations, as well as Enter, Return, and Esc key presses. We also
  172.     handle the display of the command-key hints if the command key is down,
  173.     and the drawing of the frames around the OK button and the icon.
  174.  
  175.     The first thing this routine does is show the command-key hints if the
  176.     command key is down. Since the user can press the command key at any time,
  177.     we check on Null events to see if it is down. If so, we show the hints.
  178.     Otherwise, we remove the hints from the screen. All of this hint showing
  179.     and hiding is handled by calling FlipItems with a Boolean indicating
  180.     whether we want the hints shown or not.
  181.  
  182.     If the user presses a key, we’d like to take a look at it and see if we
  183.     want to act on it. If the user presses Return or Enter, we simulate a
  184.     click on the OK button. If the user presses Command-Period or Esc, we
  185.     simulate a click on the Cancel button. If the user presses one of the
  186.     special command keys that corresponds to a radio button or checkbox, we
  187.     simulate a click on one of those. All clicks are simulated by setting
  188.     “itemHit” to the ID number of the item we want to click on.
  189.  
  190.     We also handle update events. This is so we can frame the OK button and
  191.     the icon. We put a frame around the OK button because all good Macintosh
  192.     programs should frame the button that gets selected when the user presses
  193.     Return or Enter. We frame the icon because one of my friends says it looks
  194.     better that way.
  195.  
  196.     Notice that we frame the OK button a little differently than the way
  197.     Inside Mac says to. On page I-407, it tells us to draw a 3 pixel thick
  198.     roundrect with corner radii of 16 pixels. However, if you’ve ever followed
  199.     that advice for really big buttons, you’ll see that the result looks
  200.     really dumb. This is because the buttons themselves are not always drawn
  201.     with a 16 pixel radius, which means that the button and frame don’t always
  202.     nest correctly. Therefore, we use the same algorithm the Control Manager
  203.     uses to determine the corner radius, which is to take the height of the
  204.     button and divide it by 2. However, we don’t let the value for the radius
  205.     get too small, or else it starts looking stupid again. So we make sure the
  206.     vertical radius is always at least 16 pixels; this seems to look pretty
  207.     good.
  208.  
  209.     Finally, a note on the Boolean value we have to return for this function.
  210.     This Boolean value is a signal to the Dialog Manager. If we return TRUE,
  211.     we are telling the Dialog Manager that we did everything that needed to be
  212.     done for that particular event, and that it doesn’t need to do anything
  213.     except return to its event loop and call GetNextEvent. However, if we
  214.     return FALSE, we tell the dialog manager to do what it would normally do.
  215.     So in the case of the update event, we _augment_ what is normally done by
  216.     drawing our stuff, and then returning FALSE to tell the Dialog Manager to
  217.     update everything _it_ is responsible for. In the case of Null events, we
  218.     handling the drawing of our command-key hints, and then return FALSE to
  219.     the Dialog Manager to let it do whatever it needs to do at idle time, like
  220.     blink the insertion point. On the other hand, if the user presses Return,
  221.     Enter, Esc, or one of the magic Command keys we look out for, we return
  222.     TRUE because we don’t want the Dialog Manager to do what it would normally
  223.     do with those key presses (which is to enter them into any EditText items
  224.     which might be active).
  225.  
  226. *******************************************************************************/
  227. pascal Boolean OurFilter(DialogPtr dlg, EventRecord *event, short *itemHit)
  228. {
  229.     short    iKind;
  230.     Handle    iHandle;
  231.     Rect    iRect;
  232.     char    keys[] = "BHWATPFSR";
  233.     char    key;
  234.     short    loopy;
  235.     short    radius;
  236.  
  237.     switch (event->what) {
  238.  
  239.         case nullEvent:
  240.             if (event->modifiers & cmdKey)            /* Command key down */
  241.                 FlipItems(dlg, TRUE);
  242.             else
  243.                 FlipItems(dlg, FALSE);
  244.             return FALSE;
  245.             break;
  246.  
  247.         case keyDown:
  248.         case autoKey:
  249.             key = event->message & charCodeMask;
  250.             if (event->modifiers & cmdKey) {        /* Command key down */
  251.                 key = UprChar(key);
  252.                 if (key == kPeriod) {
  253.                     *itemHit = cancel;
  254.                     FlashDialogItem(dlg, *itemHit);
  255.                 }
  256.                 for (loopy = cstrlen(keys) - 1 ; loopy >= 0 ; --loopy) {
  257.                     if (key == keys[loopy]) {
  258.                         *itemHit = loopy + kFirstRadio;
  259.                     }
  260.                 }
  261.                 return TRUE;            /* This says we handle ALL command keys */
  262.             } else {
  263.                 if ((key == kReturn) || (key == kEnter)) {
  264.                     *itemHit = ok;
  265.                     FlashDialogItem(dlg, *itemHit);
  266.                     return TRUE;
  267.                 }
  268.                 if (key == kEscape) {
  269.                     *itemHit = cancel;
  270.                     FlashDialogItem(dlg, *itemHit);
  271.                     return TRUE;
  272.                 }
  273.             }
  274.             return FALSE;
  275.  
  276.         case updateEvt:
  277.             SetPort(dlg);
  278.             GetDItem(dlg, ok, &iKind, &iHandle, &iRect);
  279.             InsetRect(&iRect, -4, -4);
  280.             radius = (iRect.bottom - iRect.top) / 2;
  281.             if (radius < 16)
  282.                 radius = 16;
  283.             PenNormal();
  284.             PenSize(3,3);
  285.             FrameRoundRect(&iRect, radius, radius);
  286.  
  287.             GetDItem(dlg, kIconID, &iKind, &iHandle, &iRect);
  288.  
  289.             InsetRect(&iRect, -3, -3);
  290.             PenSize(2, 2);
  291.             FrameRect(&iRect);
  292.  
  293.             return FALSE;
  294.  
  295.         default:
  296.             return FALSE;
  297.     }
  298. }
  299.  
  300.  
  301. /*******************************************************************************
  302.  
  303.     ShowModeless
  304.  
  305.     If we haven’t already done so, create the modeless dialog. Next, show the
  306.     dialog, bring it to front, and make it the current grafPort just for luck.
  307.  
  308.  
  309. *******************************************************************************/
  310. void    ShowModeless(void)
  311. {
  312.     if (gModelessDialog == NIL) {
  313.         gModelessDialog = GetNewDialog(kModelessDialog, NIL, (WindowPtr) -1);
  314.     }
  315.  
  316.     ShowWindow(gModelessDialog);
  317.     SelectWindow(gModelessDialog);
  318.     SetPort(gModelessDialog);
  319.  
  320.     AdjustMenus();
  321. }
  322.  
  323.  
  324. /*******************************************************************************
  325.  
  326.     StartMovable
  327.  
  328.     Kicks off our movable modal dialog that has a progress indicator. First,
  329.     we check to see if we already have such a dialog lying around. If not,
  330.     then we create one, and save its pointer in a global variable for the next
  331.     time we need this dialog. After the dialog is created, we set the string
  332.     that indicates what we’re doing. Once all that’s done, we show the window
  333.     and bring it to the front. Finally, we initialize our progress indicator
  334.     routines, and set a global flag telling a routine elsewhere in this file
  335.     (DoMainLoopTasks) that it should get off its duff and start doing what we
  336.     pay it for.
  337.  
  338. *******************************************************************************/
  339. void    StartMovable(void)
  340. {
  341.     short    iType;
  342.     Handle    iHandle;
  343.     Rect    iRect;
  344.     Str255    theString;
  345.     short    itemHit;
  346.  
  347.     if (gMovableModalDialog == NIL) {
  348.         gMovableModalDialog = GetNewDialog(kMovableModalDialog, NIL, (WindowPtr) -1);
  349.  
  350.         GetIndString(theString, kDialogStrings, kErasingDisk);
  351.         GetDItem(gMovableModalDialog, kMMText, &iType, &iHandle, &iRect);
  352.         SetIText(iHandle, theString);
  353.     }
  354.  
  355.     ShowWindow(gMovableModalDialog);
  356.     SelectWindow(gMovableModalDialog);
  357.     SetPort(gMovableModalDialog);
  358.  
  359.     GetDItem(gMovableModalDialog, kMMProgressBar, &iType, &iHandle, &iRect);
  360.     InitProgressIndicator(gMovableModalDialog, iRect, 500);
  361.  
  362.     gPeriodicTask = TRUE;
  363.     gInModalState = TRUE;
  364.     AdjustMenus();
  365. }
  366.  
  367.  
  368. /*******************************************************************************
  369.  
  370.     Utility dialog routines
  371.  
  372. *******************************************************************************/
  373.  
  374. /*******************************************************************************
  375.  
  376.     FlashDialogItem
  377.  
  378.     Utility to quickly flash a button in the dialog. We highlight the button
  379.     by calling HiliteControl, waiting a little bit so that the user can see
  380.     the button highlighted, and then turning off the button by calling
  381.     HiliteControl again.
  382.  
  383.     This routine assumes that the item we want to flash is backed up by a
  384.     Control Manager control, and really works best if the item is a simple
  385.     button control.
  386.  
  387. *******************************************************************************/
  388. void    FlashDialogItem(DialogPtr dlg, short itemToFlash)
  389. {
  390.     short    iKind;
  391.     Handle    iHandle;
  392.     Rect    iRect;
  393.     long    ignored;
  394.  
  395.     GetDItem(dlg, itemToFlash, &iKind, &iHandle, &iRect);
  396.     HiliteControl((ControlHandle) iHandle, 1);
  397.     Delay(8, &ignored);
  398.     HiliteControl((ControlHandle) iHandle, 0);
  399. }
  400.  
  401.  
  402. /*******************************************************************************
  403.  
  404.     SetDialogItemState
  405.  
  406.     Utility to set the selected state of a dialog item. This is really
  407.     intended to be used on dialog items that are backed up by Control Manager
  408.     controls only.
  409.  
  410. *******************************************************************************/
  411. void    SetDialogItemState(DialogPtr dlg, short controlNumber, short value)
  412. {
  413.     short    iKind;
  414.     Handle    iHandle;
  415.     Rect    iRect;
  416.  
  417.     GetDItem(dlg, controlNumber, &iKind, &iHandle, &iRect);
  418.     SetCtlValue((ControlHandle) iHandle, value);
  419. }
  420.  
  421.  
  422. /*******************************************************************************
  423.  
  424.     GetDialogItemState
  425.  
  426.     Utility to return the selected state of a dialog item. This is really
  427.     intended to be used on dialog items that are backed up by Control Manager
  428.     controls only.
  429.  
  430. *******************************************************************************/
  431. short    GetDialogItemState(DialogPtr dlg, short controlNumber)
  432. {
  433.     short    iKind;
  434.     Handle    iHandle;
  435.     Rect    iRect;
  436.  
  437.     GetDItem(dlg, controlNumber, &iKind, &iHandle, &iRect);
  438.     return GetCtlValue((ControlHandle) iHandle);
  439. }
  440.  
  441.  
  442. /*******************************************************************************
  443.  
  444.     SetRadioButton
  445.  
  446.     Simple utility routine to set a radio button in a dialog box. We take a
  447.     look at the item we are requested to set and compare it with the item that
  448.     is currently set. If they are the same, we don’t need to do anything.
  449.     However, if they are different, we turn off the old one, and then turn on
  450.     the new one. We also remember which item we just turned on so that we can
  451.     refer to it the next time this routine is called.
  452.  
  453.     If the number identifying the currently selected button is zero, we
  454.     interpret that to be a magic number meaning that _no_ radio buttons are
  455.     currently selected. If that’s the case, there are no buttons to be turned
  456.     off, and we just turn on the new button.
  457.  
  458.     This routine is very simple in that it supports just a single family of
  459.     buttons. In other words, _all_ radio buttons in the dialog are connected
  460.     together; turning on any single button with turn off any other button in
  461.     the dialog. If you want to support multiple families of radio buttons, you
  462.     will have to do a little more work.
  463.  
  464. *******************************************************************************/
  465. void    SetRadioButton(DialogPtr dlg, short buttonNumber)
  466. {
  467.     if (buttonNumber != gCurrentRadio) {
  468.         if (gCurrentRadio != 0)
  469.             SetDialogItemState(dlg, gCurrentRadio, 0);
  470.         gCurrentRadio = buttonNumber;
  471.         if ((gCurrentRadio >= kFirstRadio) && (gCurrentRadio <= kLastRadio))
  472.             SetDialogItemState(dlg, gCurrentRadio, 1);
  473.     }
  474. }
  475.  
  476.  
  477. /*******************************************************************************
  478.  
  479.     ToggleCheckBox
  480.  
  481.     Simple utility to toggle the setting of a checkbox item in a dialog box.
  482.     We get the old setting, take a look at it so that we know what to change
  483.     it into, and then call a utility routine that sets a dialog item to an
  484.     absolute value.
  485.  
  486. *******************************************************************************/
  487. void    ToggleCheckBox(DialogPtr dlg, short buttonNumber)
  488. {
  489.     short newState;
  490.  
  491.     if (GetDialogItemState(dlg, buttonNumber) == 0)
  492.         newState = 1;
  493.     else
  494.         newState = 0;
  495.     SetDialogItemState(dlg, buttonNumber, newState);
  496. }
  497.  
  498.  
  499. /*******************************************************************************
  500.  
  501.     SetDialogItemTitle
  502.  
  503.     Change the text associated with a particular dialog item. This is a little
  504.     tricky since there are two ways to change the text. If you are dealing
  505.     with an EditText or StatText (static text) item, you must call SetIText.
  506.     If you are dealing with a dialog item that is backed up by a Control
  507.     Manager control (like a simple button, radio button, or checkbox), you
  508.     must call SetCTitle.
  509.  
  510.     We determine what kind of dialog item we are dealing with by calling
  511.     GetDItem. Returned in the “kind” parameter is a number that identifies
  512.     what sort of item we are handling. First, we strip off the upper bit,
  513.     which identifies the item as being enabled or disabled. Once that bit is
  514.     removed, we can examine the kind of the item and act accordingly.
  515.  
  516.     Notice the special handling we give to controls when we call SetCTitle.
  517.     This is to take care of “excessive flashing” as Online Companion puts it.
  518.     When you call SetCTitle, the control manager first calls HideControl to
  519.     remove the control with its old text from the screen. It then changes the
  520.     control’s title in the ControlRecord, and reshows the control by calling
  521.     ShowControl. At this point, the control is properly shown on the screen
  522.     with its correct, new title.
  523.  
  524.     However, there’s a little time bomb lurking in the works. When HideControl
  525.     was called, the Control Manager called InvalRect on the area the control
  526.     occupied. Even though ShowControl was later called on the same area and
  527.     everything is drawn correctly, that rectangle is still marked as invalid
  528.     and is incorporated into the update region for the dialog. The event loop
  529.     at the heart of ModalDialog will then get an update event for that area
  530.     and redraw the button _again_! This can cause the button to flicker and
  531.     flash more than we would like. We already know that that area is
  532.     adequately drawn, so we tell the Event Manager to hoof it by validating it
  533.     with a call to ValidRect.
  534.  
  535. *******************************************************************************/
  536. void    SetDialogItemTitle(DialogPtr dlg, short item, Str255 *newTitle)
  537. {
  538.     short    iKind;
  539.     Handle    iHandle;
  540.     Rect    iRect;
  541.  
  542.     GetDItem(dlg, item, &iKind, &iHandle, &iRect);
  543.     iKind &= ~itemDisable;                // Strip off the enable/disable bit
  544.     if ((iKind == statText) || (iKind == editText)) {
  545.         SetIText(iHandle, *newTitle);
  546.     } else {
  547.         SetCTitle((ControlHandle) iHandle, *newTitle);
  548.         SetPort(dlg);
  549.         ValidRect(&iRect);
  550.     }
  551. }
  552.  
  553.  
  554. /*******************************************************************************
  555.  
  556.     FlipItems
  557.  
  558.     This routine changes the text associated with dialog  items. Basically, we
  559.     allow the items to have two states: the flipped on state and the flipped
  560.     off state (sometimes called the “being given the bird” state). The state
  561.     the we want the items to be in is passed in the “flippedOn” parameter.
  562.  
  563.     We decide which items to change and how to change them by using a couple
  564.     of ‘STR#’ resources. Each of these resources contains one string for each
  565.     of the items in the dialog box. The first ‘STR#’ resource contains the
  566.     strings for the items when they are in the off state. The other ‘STR#’
  567.     contains the strings that get shown when the items are in the on state.
  568.  
  569.     It doesn’t make sense to change the text of all items. For example, the
  570.     icon doesn’t have any text to change. Also, the OK has only one state. To
  571.     handle items that don’t change, we enter zero-length strings in their
  572.     place in the ‘STR#’ resource. When we come to one of those strings, we
  573.     just skip over that item.
  574.  
  575.     Once we’ve changed the text of all the affected items, we make a note of
  576.     the state we’ve left them in. This way, if we call this routine to put the
  577.     items into State A, and they’re already in State A, there is nothing to
  578.     do, and we just skip out of the function.
  579.  
  580.     By the way, take a look at how we determine how many strings there are in
  581.     the ‘STR#’ resources. The format of a ‘STR#’ resource is as follows:
  582.  
  583.         short        n=number of strings in resource
  584.         x1 bytes    String #1
  585.         x2 bytes    String #2
  586.         x3 bytes    String #3
  587.         ...            ...
  588.         xn bytes    String #n
  589.  
  590.     To find the number of strings in the resource, we coerce the handle to the
  591.     resource into a pointer to a short and fetch the number. Once we have
  592.     that, we can make that many calls to GetIndString to read all the strings
  593.     one at a time.
  594.  
  595.     Notice that we make a big assumption about the relationship between the
  596.     two ‘STR#’ resources and the number of items in the dialog. In our
  597.     example, we have 26 items in the dialog and 26 strings in each ‘STR#’
  598.     resources. If any of these conditions changes, we might be in trouble.
  599.     However, you can never protect against _everything_ a user armed with
  600.     ResEdit may do, so we take our chances and assume everything is the way we
  601.     designed it to be.
  602.  
  603. *******************************************************************************/
  604. void    FlipItems(DialogPtr dlg, Boolean flippedOn)
  605. {
  606.     short    loopy;
  607.     short    numberOfStrings;
  608.     short    resourceNumber;
  609.     Handle    strHandle;
  610.     Str255    stringMeAlong;
  611.     
  612.     if (flippedOn != gCurrentFlippedState) {
  613.  
  614.         if (flippedOn)
  615.             resourceNumber = kFlippedStrings;
  616.         else
  617.             resourceNumber = kSourceStrings;
  618.  
  619.         strHandle = GetResource('STR#', resourceNumber);
  620.         numberOfStrings = **(short **) strHandle;
  621.  
  622.         for (loopy = 1 ; loopy <= numberOfStrings ; ++loopy) {
  623.             GetIndString(stringMeAlong, resourceNumber, loopy);
  624.             if (stringMeAlong[0] != 0) {
  625.                 SetDialogItemTitle(dlg, loopy, &stringMeAlong);
  626.             }
  627.         }
  628.     }
  629.     gCurrentFlippedState = flippedOn;
  630. }
  631.  
  632.  
  633. /*******************************************************************************
  634.  
  635.     cstrlen
  636.  
  637.     Return the length of a C-string (a series of characters terminated by a
  638.     NULL. We have our own copy so that we don’t have to link with ANSI.
  639.  
  640. *******************************************************************************/
  641. long cstrlen( char *s )
  642. {
  643.     long    size = -1;
  644.     char*    str = s;
  645.     
  646.     do {
  647.         ++size;
  648.     } while (*str++);
  649.     return size;
  650. }
  651.  
  652.  
  653. /*******************************************************************************
  654.  
  655.     UprChar
  656.  
  657.     Convert a lower case letter to upper case. If we’re running under System
  658.     7.0, we could just use the new UpperText() call instead.
  659.  
  660. *******************************************************************************/
  661. char UprChar( char c )
  662. {
  663.     Str255    tempString;
  664.  
  665.     tempString[0] = 1;
  666.     tempString[1] = c;
  667.     UprString(tempString, TRUE);
  668.     return tempString[1];
  669. }
  670.  
  671.